package starfield.modpack.web;

import starfield.util.JSONUtils;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.json.simple.JSONObject;
import starfield.Application;
import starfield.ParsingException;
import starfield.modpack.web.SyncingException;
import starfield.modpack.web.SyncingProtocol;
import starfield.modpack.web.SyncingTask;
import starfield.ValidationException;
import starfield.modpack.Modpack;
import starfield.modpack.WebModpackDialog;

public class WebModpack extends Modpack {
	public static final String FILENAME_LIST = "webModpacks.config";
	
	protected static final String KEY_PROTOCOL = "protocol";
	protected static final String KEY_PORT = "port";
	protected static final String KEY_HOSTNAME = "hostname";
	protected static final String KEY_REQUIRES_LOGIN = "requiresLogin";
	protected static final String KEY_USERNAME = "username";
	protected static final String KEY_PASSWORD = "password";
	
	protected SyncingProtocol protocol;
	protected int port;
	protected String hostname;
	protected boolean requiresLogin;
	protected String username;
	protected String password;
	
	protected boolean invalid;
	
	protected WebModpack(String directoryName) {
		super(directoryName);
	}
	
	public static WebModpack load(String directoryName, JSONObject connectionJson, boolean silent) throws IOException, ParsingException, SyncingException {
		WebModpack webModpack = new WebModpack(directoryName);
		webModpack.load(connectionJson, silent);
		return webModpack;
	}
	
	protected void load(JSONObject connectionJson, boolean silent) throws IOException, ParsingException, SyncingException  {
		deserializeConnection(connectionJson);
		tryReadConfig();
		loadMods();
		Application.getModpacks().add(this);
	}
	
	protected void tryReadConfig() {
		try {
			deserialize(JSONUtils.parseObject(FileUtils.readFileToString(getConfigFile())));
			invalid = false;
		} catch (IOException | ParsingException ex) {
			invalid = true;
		}
	}
	
	public static WebModpack create(WebModpackDialog dialog) throws IOException, ValidationException, SyncingException, ParsingException {
		WebModpack modpack = new WebModpack(dialog.getDirectoryName());
		
		File directory = new File(modpack.getDirectoryPath());
		
		if (directory.exists() && directory.isDirectory()) {
			throw new IOException("Modpack directory '" + directory.getName() + "' already exists.");
		} else {
			if (!directory.mkdirs()) {
				throw new IOException("Failed to find modpack directory, and failed to create one.");
			}
			
			dialog.fillModpack(modpack);
			Application.getModpacks().add(modpack);
			Application.saveWebModpacksList();
			modpack.sync(false);
			modpack.tryReadConfig();
			modpack.loadMods();
		}
		
		return modpack;
	}
	
	public void sync(boolean silent) throws SyncingException {
		SyncingTask task = protocol.createSyncingTask(this, silent);
        task.execute();
		task.showDialog();
		tryReadConfig();
		loadMods();
	}
	
	protected void deserializeConnection(JSONObject connectionJson) throws ParsingException {
		if (connectionJson.containsKey(KEY_PROTOCOL)) {
			try {
				protocol = SyncingProtocol.valufOfIgnoreCase(JSONUtils.getString(connectionJson, KEY_PROTOCOL));
			} catch (IllegalArgumentException ex) {
				throw new ParsingException("Invalid sync protocol.", ex);
			}
		} else {
			throw new ParsingException("Missing sync protocol.");
		}

		if (connectionJson.containsKey(KEY_PORT)) {
			port = JSONUtils.getInt(connectionJson, KEY_PORT);
		} else {
			port = protocol.getPort();
		}

		if (connectionJson.containsKey(KEY_HOSTNAME)) {
			hostname = JSONUtils.getString(connectionJson, KEY_HOSTNAME);
		} else {
			throw new ParsingException("Missing sync URL.");
		}

		if (connectionJson.containsKey(KEY_REQUIRES_LOGIN)) {
			requiresLogin = JSONUtils.getBoolean(connectionJson, KEY_REQUIRES_LOGIN);
		} else {
			throw new ParsingException("Missing login requirement.");
		}

		if (requiresLogin) {
			if (connectionJson.containsKey(KEY_USERNAME)) {
				username = JSONUtils.getString(connectionJson, KEY_USERNAME);
			} else {
				throw new ParsingException("Missing sync username.");
			}

			if (connectionJson.containsKey(KEY_PASSWORD)) {
				password = JSONUtils.getString(connectionJson, KEY_PASSWORD);
			} else {
				password = "";
			}
		}
	}
	
	public JSONObject serializeConnection()  {
		JSONObject connectionObject = new JSONObject();
		
		connectionObject.put(KEY_PROTOCOL, protocol.name());
		connectionObject.put(KEY_PORT, port);
		connectionObject.put(KEY_HOSTNAME, hostname);
		connectionObject.put(KEY_REQUIRES_LOGIN, requiresLogin);
		
		if (requiresLogin) {
			connectionObject.put(KEY_USERNAME, username);
			
			if (password != null && !password.equals("")) {
				connectionObject.put(KEY_PASSWORD, password);
			}
		}
		
		return connectionObject;
	}
	
	@Override
	public String toHtmlString() {
		if (invalid) {
			StringBuilder sb = new StringBuilder();

			sb.append("<h1 style='margin-top: 5px'>").append(name).append("</h1>");

			sb.append("<p style='color: red'>This web modpack is invalid because it failed to get the required files from the server. ");
			sb.append("To fix it, please edit the connection parameters and attempt a new sync.</p>");

			return sb.toString();
		} else {
			return super.toHtmlString();
		}
	}
	
	public static String getListFilePath() {
		return System.getProperty("user.dir") + File.separator + FILENAME_LIST;
	}

	public SyncingProtocol getProtocol() {
		return protocol;
	}

	public void setProtocol(SyncingProtocol protocol) {
		this.protocol = protocol;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public String getHostname() {
		return hostname;
	}

	public void setHostname(String hostname) {
		this.hostname = hostname;
	}

	public boolean requiresLogin() {
		return requiresLogin;
	}

	public void setRequiresLogin(boolean requiresLogin) {
		this.requiresLogin = requiresLogin;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public boolean isInvalid() {
		return invalid;
	}

	public void setInvalid(boolean invalid) {
		this.invalid = invalid;
	}
}
